/*
   Copyright (c) [2023] [魔凤啸天]
   [MFUI] is licensed under Mulan PSL v2.
   You can use this software according to the terms and conditions of the Mulan PSL v2. 
   You may obtain a copy of Mulan PSL v2 at:
            http://license.coscl.org.cn/MulanPSL2 
   THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.  
   See the Mulan PSL v2 for more details.  
*/
#include "textutil.hpp"

namespace MFUI{
	const std::string emptytext;
	void TextNodeBase::settext(const std::string& text){
		this->text=text;
		_updatetext();
	}
	const std::string& TextNodeBase::gettext()const{return text;}
	void TextNodeBase::settextcolor(SDL_Color color){
		this->textcolor=color;
		_updatetext();
	}
	SDL_Color TextNodeBase::gettextcolor()const{return textcolor;}
	void TextNodeBase::setbackground(SDL_Color color){
		this->background=color;
		_updatebase();
	}
	SDL_Color TextNodeBase::getbackground()const{return background;}
	void TextNodeBase::setfontsize(int size){
		fontsize=size;
		_updatetext();
	}
	int TextNodeBase::getfontsize()const{return fontsize;}
	void TextNodeBase::setfont(Font* font){
		this->font=font;
		_updatetext();
	}
	Font* TextNodeBase::getfont()const{return font;}
	TextNodeBase::TextNodeBase(const std::string& text,Font* font,int fontsize,
	SDL_Color textcolor,SDL_Color background,Align align):text(text),font(font),
	fontsize(fontsize),textcolor(textcolor),background(background),align(align)
	{textrect.x=0,textrect.y=0;}
	size_t utilreplace(std::string& src,const std::string& target,const std::string& t,size_t n){
		if(n){
			size_t replaced=0;
			std::string s;
			std::string::iterator begin=src.begin(),cur=begin;
			size_t f=0;
			while(replaced<n){
				f=src.find(target,f);
				if(f!=std::string::npos){
					s.append(cur,cur+f);
					s.append(t);
					f+=target.size();
					cur=begin+f;
					replaced++;
					continue;
				}
				s.append(cur,src.end());
				src=s;
				return replaced;
			}
			s.append(cur,src.end());
			src=s;
			return replaced;
		}
		return 0;
	}
	void utilreplace(std::string& src,size_t begin,size_t end,const char* t){
		auto s=src.c_str();
		auto l=strlen(t);
		auto size=src.size()+l-(end-begin);
		char buffer[size];
		memcpy(buffer,s,begin);
		memcpy(buffer+begin,t,l);
		memcpy(buffer+begin+l,s+end,src.size()-end);
		src.assign(buffer,size);
	}
	void TextNodeBase::_tryrendertext(Renderer* r,SDL_FRect* rect){
		font->hsize(*rect,text,fontsize,textrect);
		r->setdrawcolor(background);
		r->fillrectF(rect);
		SDL_FRect _;
		getalignrect(rect,&textrect,align,&_);
		font->hdraw(r,_,text,fontsize,textcolor);
	}
	void TextNodeBase::setalign(Align align){this->align=align;_updatebase();}
	Align TextNodeBase::getalign()const{return align;}
	void utilreplacestring(std::string& src,size_t begin,size_t end,const std::string& t){
		auto s=src.c_str();
		auto l=t.size();
		auto size=src.size()+l-(end-begin);
		char buffer[size];
		memcpy(buffer,s,begin);
		memcpy(buffer+begin,t.c_str(),l);
		memcpy(buffer+begin+l,s+end,src.size()-end);
		src.assign(buffer,size);
	}
	void utilmoveleft(std::string& src,size_t begin,size_t end,size_t pos){
		auto size=src.size();
		char buffer[size];
		auto s=src.c_str();
		auto delta=end-begin;
		auto m=begin-pos;
		memcpy(buffer,s,pos);
		memcpy(buffer+pos,s+begin,delta);
		memcpy(buffer+pos+delta,s+pos,m);
		memcpy(buffer+pos+delta+m,s+end,size-end);
		src.assign(buffer,size);
	}
	void utilmoveright(std::string& src,size_t begin,size_t end,size_t pos){
		auto size=src.size();
		char buffer[size];
		auto s=src.c_str();
		auto delta=pos-end;
		auto m=end-begin;
		memcpy(buffer,s,begin);
		memcpy(buffer+begin,s+end,delta);
		memcpy(buffer+begin+delta,s+begin,m);
		memcpy(buffer+begin+delta+m,s+pos,size-pos);
		src.assign(buffer,size);
	}
	Font::Cache::~Cache(){for(auto& n:words)SDL_DestroyTexture(n.second);}
	Font::Font(TTF_Font* font):font(font){}
	Font::~Font(){for(auto& n:cache)delete n.second;}
	void Font::removecache(int fontsize){
		auto f=cache.find(fontsize);
		if(f!=cache.end()){
			delete f->second;
			cache.erase(f);
		}
	}
	void Font::setfont(TTF_Font* f){
		if(f!=font){
			font=f;
			for(auto& n:cache)delete n.second;
			cache.clear();
		}
	}
	SDL_Texture* Font::buildcache(Renderer* r,const char* begin,const char* end,int fontsize){
		auto e=cache.emplace(fontsize,nullptr);
		Cache* v;
		if(e.second){
			v=new Cache();
			e.first->second=v;
		}
		else v=e.first->second;
		auto& m=v->words;
		std::string text(begin,end);
		auto k=m.emplace(text,nullptr);
		if(k.second){
			auto s=TTF_RenderUTF8_Blended(font,text.c_str(),{255,255,255,255});
			k.first->second=r->texturefromsurface(s,SDL_BLENDMODE_BLEND);
			SDL_FreeSurface(s);
		}
		return k.first->second;
	}
	inline void drawword(Renderer* r,const SDL_FRect& dst,SDL_Texture* word,SDL_Color color){
		SDL_SetTextureColorMod(word,color.r,color.g,color.b);
		SDL_SetTextureAlphaMod(word,color.a);
		SDL_Rect rect{0,0,(int)dst.w,(int)dst.h};
		r->copyF(word,&rect,&dst);
	}
	void Font::hdraw(Renderer* r,const SDL_FRect& dst,const std::string& text,int fontsize,
	SDL_Color color,bool cuttail){
		hdraw(r,dst,text.begin().base(),text.end().base(),fontsize,color,cuttail);
	}
	void Font::hdraw(Renderer* r,const SDL_FRect& dst,const char* s,const char* end,int fontsize,
	SDL_Color color,bool cuttail){
		size_t i=0,e=end-s;
		float dw=dst.w,sum=0.f;
		TTF_SetFontSize(font,fontsize);
		SDL_FRect _{dst.x,dst.y,0.f,std::min(dst.h,(float)TTF_FontHeight(font))};
		int w;
		while(i!=e){
			auto cur=s+i;
			auto l=utf8len(*cur);
			if(l){
				auto word=buildcache(r,cur,cur+l,fontsize);
				SDL_QueryTexture(word,nullptr,nullptr,&w,nullptr);
				_.w=w;
				auto delta=sum-dw;
				if(delta>0.f){
					if(cuttail)return;
					_.w-=delta;
					drawword(r,_,word,color);
					return;
				}
				drawword(r,_,word,color);
				sum+=_.w;
				_.x+=_.w;
				i+=l;
				continue;
			}
			return;
		}
	}
	const char* Font::hdraw(Renderer* r,const SDL_FRect& dst,const char* s,const char* end,int fontsize,
	SDL_Color color,float& outw,float& outh){
		size_t i=0,e=end-s;
		float dw=dst.w,sum=0.f;
		TTF_SetFontSize(font,fontsize);
		SDL_FRect _{dst.x,dst.y,0.f,std::min(dst.h,(float)TTF_FontHeight(font))};
		int w;
		while(i!=e){
			auto cur=s+i;
			auto l=utf8len(*cur);
			if(l){
				auto word=buildcache(r,cur,cur+l,fontsize);
				SDL_QueryTexture(word,nullptr,nullptr,&w,nullptr);
				auto _sum=sum+w;
				if(_sum>dw){
					outw=sum;
					outh=_.h;
					return cur;
				}
				_.w=w;
				drawword(r,_,word,color);
				sum=_sum;
				_.x+=_.w;
				i+=l;
				continue;
			}
			outw=sum;
			outh=_.h;
			return cur;
		}
		outw=sum;
		outh=_.h;
		return end;
	}
	void Font::hsize(const SDL_FRect& dst,const std::string& text,int fontsize,SDL_FRect& out){
		hsize(dst,text.begin().base(),text.end().base(),fontsize,out);
	}
	const char* Font::hsize(const SDL_FRect& dst,const char* begin,const char* end,int fontsize,SDL_FRect& out){
		int w;
		float sum=0.f,dw=dst.w;
		auto font=this->font;
		TTF_SetFontSize(font,fontsize);
		while(begin!=end){
			auto l=utf8len(*begin);
			if(l){
				char buffer[8]={0,0,0,0,0,0,0,0};
				for(unsigned int j=0;j<l;j++)buffer[j]=*(begin++);
				TTF_SizeUTF8(font,buffer,&w,nullptr);
				auto delta=sum-dw;
				if(delta>0.f){
					out.w=dw,out.h=std::min((float)TTF_FontHeight(font),dst.h);
					return begin-l;
				}
				sum+=w;
				continue;
			}
			out.w=sum,out.h=std::min((float)TTF_FontHeight(font),dst.h);
			return begin;
		}
		out.w=sum,out.h=std::min((float)TTF_FontHeight(font),dst.h);
		return end;
	}
	void Font::hsize(const std::string& text,int fontsize,SDL_FRect& out){
		hsize(text.begin().base(),text.end().base(),fontsize,out);
	}
	void Font::hsize(const char* begin,const char* end,int fontsize,SDL_FRect& out){
		int w;
		float sum=0.f;
		auto font=this->font;
		TTF_SetFontSize(font,fontsize);
		while(begin!=end){
			auto l=utf8len(*begin);
			if(l){
				char buffer[8]={0,0,0,0,0,0,0,0};
				for(unsigned int j=0;j<l;j++)buffer[j]=*(begin++);
				TTF_SizeUTF8(font,buffer,&w,nullptr);
				sum+=w;
				continue;
			}
			out.w=sum,out.h=TTF_FontHeight(font);
			return;
		}
		out.w=sum,out.h=TTF_FontHeight(font);
	}
	void Font::htextunits(const std::string& text,int fontsize,std::vector<TextUnit>& units){
		htextunits(text.begin().base(),text.end().base(),fontsize,units);
	}
	void Font::htextunits(const char* begin,const char* end,int fontsize,std::vector<TextUnit>& units){
		size_t i=0;
		int w;
		float sum=0.f;
		auto font=this->font;
		TTF_SetFontSize(font,fontsize);
		while(begin!=end){
			auto l=utf8len(*begin);
			if(l){
				char buffer[8]={0,0,0,0,0,0,0,0};
				for(unsigned int j=0;j<l;j++)buffer[j]=*(begin++);
				TTF_SizeUTF8(font,buffer,&w,nullptr);
				units.emplace_back(i,l,sum);
				i+=l;
				sum+=w;
				continue;
			}
			units.emplace_back(i,0,sum);
			return;
		}
		units.emplace_back(i,0,sum);
	}
	int Font::height(int fontsize){
		TTF_SetFontSize(font,fontsize);
		return TTF_FontHeight(font);
	}
}